#include <GyverOLED.h>
#include <GyverEncoder.h>
#include <Wire.h>
#include <EEPROM.h>
#include <Keyboard.h>
#include <Mouse.h>
#include <LiquidCrystal_I2C.h> 



// === НАСТРОЙКА ДЕБАГА ===
#define DEBUG_MODE 1  // Поставь 0 (или закомментируй), чтобы ПОЛНОСТЬЮ отключить дебаг

#if (DEBUG_MODE == 1)
  // Если дебаг включен, макрос DEB_PRINT превращается в обычный Serial.println
  #define DEB_PRINT(x) Serial.println(x)
  #define DEB_INIT()   \
    Serial.begin(9600); \
    while (!Serial && (millis() < 2000)); \
    Serial.println(F("=== ДЕБАГ АКТИВЕН ==="))

#else
  // Если дебаг выключен, макросы превращаются в ПУСТОТУ. 
  #define DEB_PRINT(x)
  #define DEB_INIT()
#endif


#define PRIMARY_EEPROM_WRITE_MODE 0  // Поставь 0 (или закомментируй), чтобы ПОЛНОСТЬЮ отключить дебаг

#if (PRIMARY_EEPROM_WRITE_MODE == 1)
  // Если дебаг включен, макрос WRITE_TEST_PASSES() превращается в обычный Serial.println
  #define WRITE_TEST_PASSES() fillEEPROMWithData()
#else
  // Если дебаг выключен, макросы превращаются в ПУСТОТУ. 
    #define WRITE_TEST_PASSES()
#endif
// ========================

void updateDisplay();
void fillEEPROMWithData();
String readPassword(int idx);

const int SLOT_SIZE = 50;


GyverOLED<SSD1306_128x32, OLED_BUFFER> oled;
Encoder enc(9, 8, 7);
LiquidCrystal_I2C lcd(0x27,16,2);  // Задаем адрес и размер дисплея

int counter = 1;
enum DeviceMode { MODE_INFO, MODE_WRITE, MODE_GAMEPAD };
DeviceMode currentMode = MODE_INFO;
int currentSlot = 0;

// Классический стабильный массив строк скрипта во Flash-памяти
const char pythonScript[] PROGMEM = 
"import serial, sys, time\n"
"import serial.tools.list_ports\n"
"print('--- MACROPAD 20-SLOT CONFIGURATOR ---')\n"
"ports = list(serial.tools.list_ports.comports())\n"
"if not ports:\n"
"    print('No COM ports found!'); sys.exit()\n"
"print('Available ports:')\n"
"for i, p in enumerate(ports): print(f'{i}: {p.device}')\n"
"idx = int(input('Select port index: '))\n"
"port = ports[idx].device\n"
"try:\n"
"    ser = serial.Serial(port, 115200, timeout=1)\n"
"    time.sleep(2)\n"
"    slot = int(input('Enter slot number (1-20): '))\n"
"    password = input('Enter new password: ')\n"
"    cmd = f'SET:{slot-1}:{password}\\n'\n"
"    ser.write(cmd.encode('utf-8'))\n"
"    print('Password successfully saved to Macropad!')\n"
"    ser.close()\n"
"except Exception as e:\n"
"    print(f'Error: {e}')\n"
"input('\\nPress Enter to exit...')\n";

void setup() {
  DEB_INIT(); // Инициализация порта (испарится, если DEBUG_MODE = 0)
  
  DEB_PRINT(F("Старт дисплея..."));
  oled.init();
  oled.clear();
  
  WRITE_TEST_PASSES();
  
  enc.setType(TYPE2); 
  updateDisplay();

    lcd.init();                      // Инициализация lcd    
  lcd.backlight();                 // Включаем подсветку
  lcd.setCursor(0,0);              // Устанавливаем курсор в начало 1 строки
  lcd.print("Hello, world");       // Выводим текст
  lcd.setCursor(0,1);              // Устанавливаем курсор в начало 2 строки
  lcd.print("www.robotchip.ru");   // Выводим текст
}

void loop() {
  enc.tick();

  if (enc.isTurn()) {
    DEB_PRINT(F("Поворот энкодера!"));
    if (enc.isRight() && counter < 20) counter++;
    if (enc.isLeft() && counter > 1)  counter--;

    updateDisplay();
  }

  if (enc.isClick()) {
    DEB_PRINT(F("Клик!"));
    counter = 1;
    updateDisplay();
  }


}

void updateDisplaySinus() {
  DEB_PRINT(F("Отрисовка синусоиды..."));
  
  oled.clear(); // Очищаем буфер

  // Если счетчик равен 0, деление сломает логику, приравняем к 1
  int frequency = counter;
  //if (frequency == 0) frequency = 1;

  // Цикл идет по всей ширине экрана (128 пикселей)
  for (int x = 0; x < 128; x++) {
    
    // Вычисляем Y по формуле:
    // 16 — это центр экрана 128x32 по вертикали
    // 12 — амплитуда (волна будет колебаться от 4 до 28 пикселя)
    // frequency — управляет тем, сколько волн поместится на экране
    float angle = (float)x / 10.0 * ((float)frequency / 1.0);
    int y = 16 + 12 * sin(angle);

    // Проверяем, чтобы Y случайно не вышел за границы экрана (0..31)
    if (y >= 0 && y < 32) {
      oled.dot(x, y); // Рисуем точку синусоиды
    }
  }

  // Для наглядности выведем значение счетчика в углу мелким шрифтом
  // oled.setScale(1);
  // oled.setCursor(2, 0);
  // oled.print(F("F: "));
  // oled.print(counter);

  oled.update(); // Выводим буфер на экран
    DEB_PRINT(F("   [ОК] Синусоида выведена"));

}

void updateDisplay() {
  DEB_PRINT(F("Обновление экрана..."));
  oled.clear();       
  oled.setScale(0);   
  oled.setCursor(0, 0); 
  oled.print("Slot :");
  oled.print(counter);
  oled.setCursor(0, 1); 
  oled.print(readPassword(counter));
  DEB_PRINT(("Читаем пароль номер"+ String(counter)+ " " + readPassword(counter)));
  oled.update();      
}

void fillEEPROMWithData() {
  DEB_PRINT(F("Начало записи памяти"));
  String hotkeys[4] = {"SuperSecret123!", "WorkPass2026#", "QwErTy_9876", "My_Mega_Password"};
  for (int slot = 0; slot < 4; slot++) {
    int addr = slot * SLOT_SIZE; String pass = hotkeys[slot];
    for (unsigned int i = 0; i < SLOT_SIZE - 1; i++) { EEPROM.update(addr + i, (i < pass.length()) ? pass[i] : '\0'); }
    EEPROM.update(addr + SLOT_SIZE - 1, '\0');
    DEB_PRINT(("Записан пароль" + String(slot)+ " " + pass));
  }
  for (int slot = 4; slot < 20; slot++) {
    int addr = slot * SLOT_SIZE; String pass = "ArchPass_" + String(slot + 1);
    for (unsigned int i = 0; i < SLOT_SIZE - 1; i++) { EEPROM.update(addr + i, (i < pass.length()) ? pass[i] : '\0'); }
    EEPROM.update(addr + SLOT_SIZE - 1, '\0');
    DEB_PRINT(("Записан пароль" + String(slot)+ " " + pass));
  }
}


String readPassword(int idx) {
  String p = "";
  for (int i = 0; i < SLOT_SIZE; i++) {
    char c = EEPROM.read(idx * SLOT_SIZE + i);
    if (c == '\0' || c == 255) break;
    p += c;
  }
  return p;
}